package com.tianji.learning.controller.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.cache.CategoryCache;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.client.search.SearchClient;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.controller.service.IInteractionQuestionService;
import com.tianji.learning.domain.dto.QuestionFormDTO;
import com.tianji.learning.domain.po.InteractionQuestion;
import com.tianji.learning.domain.po.InteractionReply;
import com.tianji.learning.domain.query.QuestionAdminPageQuery;
import com.tianji.learning.domain.query.QuestionPageQuery;
import com.tianji.learning.domain.vo.QuestionAdminVO;
import com.tianji.learning.domain.vo.QuestionVO;
import com.tianji.learning.mapper.InteractionQuestionMapper;
import com.tianji.learning.mapper.InteractionReplyMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 互动提问的问题表 服务实现类
 * </p>
 *
 * @author zt
 * @since 2024-09-13
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class InteractionQuestionServiceImpl extends ServiceImpl<InteractionQuestionMapper, InteractionQuestion> implements IInteractionQuestionService {

    private final InteractionReplyMapper replyMapper;
    private final UserClient userClient;
    private final SearchClient searchClient;
    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final CategoryCache categoryCache;
    @Override
    @Transactional
    public void saveQuestion(QuestionFormDTO questionDTO) {
        //1.获取登录用户id
        Long userId = UserContext.getUser();
        //2.数据转换
        InteractionQuestion question = BeanUtils.copyBean(questionDTO, InteractionQuestion.class);
        //3.补充数据
        question.setUserId(userId);
        //4.保存数据
        save(question);
    }

    @Override
    @Transactional
    public void updateQuestion(Long id, QuestionFormDTO questionDTO) {
        //1.校验字段是否为空，因为之前没有用@Valid注解校验
        if(StringUtils.isBlank(questionDTO.getTitle())||StringUtils.isBlank(questionDTO.getDescription())||questionDTO.getAnonymity()==null){
            throw new BadRequestException("非法参数");
        }
        //2.校验id
        InteractionQuestion question = this.getById(id);
        if(question==null){
            throw new BadRequestException("非法参数");
        }
        //健壮性检查：修改只能修改自己的问题
        Long userId = UserContext.getUser();
        if(!userId.equals(question.getUserId())){
            throw new BadRequestException("不能修改别人的互动问题");
        }
        //3.dto转po
        question.setTitle(questionDTO.getTitle());
        question.setDescription(questionDTO.getDescription());
        question.setAnonymity(questionDTO.getAnonymity());
        //4.修改
        this.updateById(question);
    }

    /**
     * 分页查询回答
     * @param query
     * @return
     */
    @Override
    public PageDTO<QuestionVO> queryQuestionPage(QuestionPageQuery query) {
        //1、校验参数，课程id不能为空
        Long courseId = query.getCourseId();
        if(courseId==null){
            throw new BadRequestException("课程id不能为空");
        }
        //2、分页查询互动问题interaction_question
        //条件:courseId onlyMine为true才会加userId 小节id不为空 hidden为false 分页查询 按提问时间倒序
        Page<InteractionQuestion> page = this.lambdaQuery()
                //课程id
                .eq(InteractionQuestion::getCourseId, courseId)
                //当前端选择只查看自己的回答时，加上用户id的条件
                .eq(query.getOnlyMine(), InteractionQuestion::getUserId, UserContext.getUser())
                .eq(query.getSectionId() != null, InteractionQuestion::getSectionId, query.getSectionId())
                //查询选择不隐藏的回答
                .eq(InteractionQuestion::getHidden, false)
                //page表示结果是一个分页结果
                //参数query.toMpPageDefaultSortByCreateTimeDesc()表示结果按创造时间倒序排序，回答越新越在前面
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        //获取page结果中的record字段，这个才是数据库中查询到的结果的list集合
        List<InteractionQuestion> records = page.getRecords();
        if(CollUtils.isEmpty(records)){
            //查询结果为空，直接返回空的分页数据
            return PageDTO.empty(page);
        }
        //3、对查询的结果继续处理，因为返回的结果还需要其他的不在InteractionQuestion中可以查询到的数据
        //还需要封装的结果包括：最新一次回答的内容，最近一次回答者的昵称，提问者的昵称和头像
        /*
        如果这里不是分页查询，返回的只是一个结果，那么这里的查询是很简单的
        因为InteractionQuestion中是有最近一次回答的id和提问者id，直接用这些信息查询数据表就行
        这里是分页查询，需要查询多个数据，再一一对应封装，就需要更复杂的操作了
        需要先根据id批量查询，因为批量查询是一次请求，循环查询效率更快
        将结果转化为map，便于数据的一一对应，便于数据的使用
         */
        //3.1获取最近一次回答的信息，包括回答的内容和回答者的昵称
        //创建集合对象的目的是方便再数据库中根据集合批量查询，使用in关键字作为条件
        Set<Long> latestAnswerIds = new HashSet<>();//互动问题的最新回答id集合
        Set<Long> userIds = new HashSet<>();//互动问题的用户id集合，包括非匿名的提问者和回答者userId
        //遍历结果records，将数据存入到两个集合里面
        for (InteractionQuestion record : records) {
            if(record.getAnonymity()==false){
                //非匿名回答，需要查询提问者的id，加入userIds集合
                userIds.add(record.getUserId());//提问者id
            }
            latestAnswerIds.add(record.getLatestAnswerId());//最近一次回答的id
        }
        //创建回答信息Map，key为回答id，value回答信息，map的作用是将回答id和回答信息一一对应，便于集合的查询和处理
        Map<Long, InteractionReply> replyMap = new HashMap<>();
        if(CollUtils.isNotEmpty(latestAnswerIds)){
            //根据latestAnswerIds在数据库中批量查询
            List<InteractionReply> replyList = replyMapper.selectBatchIds(latestAnswerIds);
            for (InteractionReply reply : replyList) {
                replyMap.put(reply.getId(),reply);
                if(reply.getAnonymity()){//回答非匿名
                    userIds.add(reply.getUserId());//回答者的userId也要查询，所以也放到userIds里面
                }
            }
        }
        //3.2远程调用用户服务，根据userIds查询用户集合
        List<UserDTO> userDTOList = userClient.queryUserByIds(userIds);
        //通过stream流的方式，转化成Map数据
        Map<Long, UserDTO> userDTOMap = userDTOList.stream().collect(Collectors.toMap(UserDTO::getId, userDTO -> userDTO));
        //4、封装成vo集合返回
        List<QuestionVO> list = new ArrayList<>();
        for (InteractionQuestion record : records) {
            QuestionVO vo = BeanUtils.copyBean(record, QuestionVO.class);
            InteractionReply reply = replyMap.get(record.getLatestAnswerId());
            if(reply!=null){
                if (!reply.getAnonymity()){//最新回答如果是非匿名才设置最新回答者的昵称
                    UserDTO userDTO = userDTOMap.get(record.getUserId());
                    //设置回答者昵称
                    vo.setLatestReplyUser(userDTO.getName());
                }
            }
            vo.setLatestReplyContent(reply.getContent());
            if(vo.getAnonymity()!=false){
                //非匿名的回答，要封装用户数据
                UserDTO userDTO = userDTOMap.get(record.getUserId());
                vo.setUserIcon(userDTO.getIcon());
                vo.setUserName(userDTO.getName());
            }
            list.add(vo);
        }
        return PageDTO.of(page,list);
    }

    @Override
    public QuestionVO queryQuestionById(Long id) {
        //校验
        if(id==null){
            throw new BadRequestException("非法参数");
        }
        //1.根据id查询数据
        InteractionQuestion question = this.getById(id);
        //2.数据校验
        if(question==null||question.getHidden()){
            return null;
        }
        //3.查询提问者信息
        UserDTO userDTO = null;
        if(!question.getAnonymity()){
            userDTO = userClient.queryUserById(question.getUserId());
        }
        //4.封装VO
        QuestionVO vo = BeanUtils.copyBean(question, QuestionVO.class);
        if(userDTO!=null){
            vo.setUserName(userDTO.getName());
            vo.setUserIcon(userDTO.getIcon());
        }
        return vo;
    }

    @Override
    public void deleteQuestionById(Long id) {
        //1.获取用户id
        Long userId = UserContext.getUser();
        //2.查询id数据
        InteractionQuestion question = this.getById(id);
        if(question==null){
            return;
        }
        if(!question.getUserId().equals(userId)){
            throw new BadRequestException("不是当前用户的id，不能删除");
        }
        //3.查询问题的评论
        LambdaQueryWrapper<InteractionReply> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(InteractionReply::getQuestionId,id);
        List<InteractionReply> replyList = replyMapper.selectList(wrapper);
        //4.删除评论
        if(replyList!=null&&replyList.size()>0){
            for (InteractionReply reply : replyList) {
                replyMapper.deleteById(reply.getId());
            }
        }
        //5.删除问题
        this.removeById(id);
    }

    /**
     * 管理端的分页查询，是根据关键字检索的
     * @param query
     * @return
     */
    @Override
    public PageDTO<QuestionAdminVO> queryQuestionPageAdmin(QuestionAdminPageQuery query) {
        //1、根据query中的courseName模糊匹配获得课程id集合
        //创建课程id集合对象
        List<Long> couserIdList = null;
        if(StringUtils.isNotBlank(query.getCourseName())){
            //调用搜索模块的微服务，更具courseName模糊查询出对应的课程id集合
            couserIdList = searchClient.queryCoursesIdByName(query.getCourseName());
            if(CollUtils.isEmpty(couserIdList)){
                return PageDTO.empty(0L,0L);
            }
        }
        //2、分页查询
        //获取query中的各类属性，用于查询
        Integer status = query.getStatus();
        LocalDateTime beginTime = query.getBeginTime();
        LocalDateTime endTime = query.getEndTime();
        Page<InteractionQuestion> page = this.lambdaQuery()
                .in(couserIdList != null, InteractionQuestion::getCourseId, couserIdList)
                .eq(status != null, InteractionQuestion::getStatus, status)
                //表示查找问答的创建时间的区间条件
                .gt(beginTime != null, InteractionQuestion::getCreateTime, beginTime)
                .lt(endTime != null, InteractionQuestion::getCreateTime, endTime)
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        List<InteractionQuestion> records = page.getRecords();
        if(CollUtils.isEmpty(records)){
            return PageDTO.empty(page);
        }
        //3、vo还需要的查询的数据是：用户数据，课程数据，章节数据
        //先创建三个Set集合保存records里面相应的id集合，用于数据的批量查询
        Set<Long> userIds = new HashSet<>();
        Set<Long> couserIds = new HashSet<>();
        Set<Long> chaperIds = new HashSet<>();//章节id保存章节和小节id，因为二者处理相同，可以复用
        //将record中的数据转化到set集合里面
        for (InteractionQuestion record : records) {
            //这里不用管匿名不匿名了，管理员权限大，匿名也可以查看
            userIds.add(record.getUserId());
            couserIds.add(record.getCourseId());
            chaperIds.add(record.getChapterId());
            chaperIds.add(record.getSectionId());
        }
        //4、查询用户数据
        //远程调用用户模块查询用户信息
        List<UserDTO> userDTOList = userClient.queryUserByIds(userIds);
        //将用户信息转化为map，便于使用，map的key为userId，value为userDTO
        //使用stream流的方式转化
        Map<Long, UserDTO> userDTOMap = new HashMap<>();
        if(CollUtils.isNotEmpty(userDTOList)){
            userDTOMap = userDTOList.stream().collect(Collectors.toMap(UserDTO::getId, userDTO -> userDTO));
        }
        //4、查询课程数据
        //远程调用课程模块的接口，查询课程信息
        List<CourseSimpleInfoDTO> courseList = courseClient.getSimpleInfoList(couserIds);
        //转化为map的形式，便于数据的处理
        Map<Long, CourseSimpleInfoDTO> courseMap = new HashMap<>();
        if(CollUtils.isNotEmpty(courseList)){
            courseMap = courseList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, courseSimpleInfoDTO -> courseSimpleInfoDTO));
        }
        //5、查询章节数据
        //远程调用目录模块的接口，查询目录信息，包括课程章节和课程小节
        List<CataSimpleInfoDTO> cataList = catalogueClient.batchQueryCatalogue(chaperIds);
        //转化为map，这里map中的value只需要保存目录名就行
        Map<Long, String> cataMap = new HashMap<>();
        if(CollUtils.isNotEmpty(cataList)){
            cataMap = cataList.stream().collect(Collectors.toMap(CataSimpleInfoDTO::getId, cata -> cata.getName()));
        }
        //6、封装vo
        //创建返回的list对象
        List<QuestionAdminVO> list = new ArrayList<>();
        //循环遍历record进行数据的封装，po转vo，po是数据库的实体类，vo是返回数据的实体类
        for (InteractionQuestion record : records) {
            QuestionAdminVO vo = BeanUtils.copyBean(record, QuestionAdminVO.class);
            UserDTO userDTO = userDTOMap.get(record.getUserId());
            if(userDTO!=null){
                vo.setUserName(userDTO.getName());
            }
            CourseSimpleInfoDTO courseSimpleInfoDTO = courseMap.get(record.getCourseId());
            if(courseSimpleInfoDTO!=null){
                vo.setCourseName(courseSimpleInfoDTO.getName());
                //获取课程的三级分类id
                List<Long> categoryIds = courseSimpleInfoDTO.getCategoryIds();
                //操作本地缓存caffeine的接口，根据课程的三级分类id集合查询出每个分类id对应的名称
                String categoryNames = categoryCache.getCategoryNames(categoryIds);
                //封装vo
                vo.setCategoryName(categoryNames);
            }
            vo.setChapterName(cataMap.getOrDefault(record.getChapterId(),""));
            vo.setSectionName(cataMap.getOrDefault(record.getSectionId(),""));
            list.add(vo);
        }
       return PageDTO.of(page,list);
    }

    @Override
    public void hiddenQuestionAdmin(Long id, Boolean hidden) {
        //1.校验id
        if(id==null){
            throw new BadRequestException("参数非法");
        }
        //2.查询问题是否存在
        InteractionQuestion question = this.getById(id);
        if(question==null){
            throw new BadRequestException("问题不存在");
        }
        //3.修改状态
        question.setHidden(hidden);
        this.updateById(question);
    }

    @Override
    public QuestionAdminVO queryQuestionByIdAdmin(Long id) {
        //1.校验id
        if(id==null){
            throw new BadRequestException("参数错误");
        }
        //2.查询问题
        InteractionQuestion question = this.getById(id);
        if(question==null){
            throw new BadRequestException("问题不存在");
        }
        QuestionAdminVO vo = BeanUtils.copyBean(question, QuestionAdminVO.class);
        //3.获取提问者信息
        UserDTO user = userClient.queryUserById(question.getUserId());
        vo.setUserName(user.getName());
        vo.setUserIcon(user.getIcon());
        //4.课程负责老师
        CourseFullInfoDTO course = courseClient.getCourseInfoById(question.getCourseId(), true, true);
        //todo
        return null;
    }
}
